package pages;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import templates.Template;

import database.DBException;
import database.DBUtils;

/**
 * Servlet implementation class userInfoForAdmin
 */
public class UserInfoForAdmin extends HttpServlet {
	private static final long serialVersionUID = 1L;

	private static final String templateName = "userInfoForAdmin";
	private static final String permissionsTemplateName = "permissions";
	private static final String permissionsRowTemplateName = "permission_row";
	private static final String enableDisableButtonTemplateName = "enable_disable_button";
	private static final String pageTitle = "User Info";

	/**
	 * @see HttpServlet#HttpServlet()
	 */
	public UserInfoForAdmin() {
		super();
	}

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		String criticalError = "";
		try {
			String userLogin = LMUtils.getUserLogin(request);
			String showUserLogin = LMUtils.getRequestParameterValueNotEmpty(
					request, "showUserLogin");
			Template template = new Template(templateName);
			Template userInfoTemplate = userPages.UserInfo.userInfoTemplate(
					showUserLogin, getServletContext());
			template.addChange("userLogin", showUserLogin);
			template.addChange("error", LMUtils.getRequestAttribute(request,
					"error"));
			template.addChange("errorPassword", LMUtils.getRequestAttribute(
					request, "errorPassword"));
			template.addChange("new_passwordError", LMUtils
					.getRequestAttribute(request, "new_passwordError"));
			template.addChange("c_passwordError", LMUtils.getRequestAttribute(
					request, "c_passwordError"));

			template.addChange("name", userInfoTemplate.getChange("name"));
			template.addChange("phone", userInfoTemplate.getChange("phone"));
			template
					.addChange("address", userInfoTemplate.getChange("address"));

			template.addChange("permissions",
					generatePermissions(showUserLogin));
			template.addChange("showUserLogin", showUserLogin);

			template.addChange("EnableDisableButton", generageEnableDisable(
					userLogin, showUserLogin));
			// template = LMUtils.adminMenu(template);
			response.getWriter().print(
					template.createPage(pageTitle, LMUtils
							.getUserLogin(request)));
		} catch (MissingParameterException e) {
			criticalError = "UserID is not defined";
		} catch (DBException e) {
			criticalError = e.getMessage();
		} catch (InternalErrorException e) {
			criticalError = e.getMessage();
		}
		if (!criticalError.isEmpty()) {
			ErrorPage.printErrorPage(criticalError, request, response);
		}
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	protected void doPost(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		LMUtils.setRequestAttribute(request, "error", "");
		try {
			if (LMUtils.getRequestParameterValueNotEmpty(request, "editInfo") != null) {
				// edit Info
				try {
					// getting all the fields
					String showUserLogin = LMUtils
							.getRequestParameterValueNotEmpty(request,
									"showUserLogin");
					String newUserName = LMUtils
							.getRequestParameterValueNotEmpty(request, "name");
					String newPhone = LMUtils.getRequestParameterValue(request,
							"phone");
					String newAddress = LMUtils.getRequestParameterValue(
							request, "address");

					userPages.UserInfo.changeUserInfo(showUserLogin,
							newUserName, newPhone, newAddress);
				} catch (MissingParameterException e) {
					LMUtils.setRequestAttribute(request, "error",
							"Please fill in all the fields");
				}
			}
		} catch (Exception e) {
			// no info edit was asked
		}
		try {
			if (LMUtils.getRequestParameterValueNotEmpty(request,
					"changePermissions") != null) {
				try {
					String[] permissions = request
							.getParameterValues("permissions[]");
					String showUserLogin = LMUtils
							.getRequestParameterValueNotEmpty(request,
									"showUserLogin");
					changePermissions(showUserLogin, permissions);
				} catch (InternalErrorException e) {
					LMUtils.setRequestAttribute(request, "error", e
							.getMessage());
				} catch (DBException e) {
					LMUtils.setRequestAttribute(request, "error", e
							.getMessage());
				}
			}
		} catch (Exception e) {
			// no permissions edit was asked
		}

		// enable / disable
		try {
			if (LMUtils.getRequestParameterValueNotEmpty(request,
					"enableDisable") != null) {
				try {
					String showUserLogin = LMUtils
							.getRequestParameterValueNotEmpty(request,
									"showUserLogin");
					changeEnabled(showUserLogin);
				} catch (InternalErrorException e) {
					LMUtils.setRequestAttribute(request, "error", e
							.getMessage());
				} catch (DBException e) {
					LMUtils.setRequestAttribute(request, "error", e
							.getMessage());
				}
			}
		} catch (Exception e) {
			// no status edit was asked
		}

		// delete reservations
		try {
			if (LMUtils.getRequestParameterValueNotEmpty(request,
					"deleteReservations") != null) {
				try {
					String showUserLogin = LMUtils
							.getRequestParameterValueNotEmpty(request,
									"showUserLogin");
					deleteAllReservations(showUserLogin);
					LMUtils.setRequestAttribute(request, "error", "DONE!");
				} catch (InternalErrorException e) {
					LMUtils.setRequestAttribute(request, "error", e
							.getMessage());
				} catch (DBException e) {
					LMUtils.setRequestAttribute(request, "error", e
							.getMessage());
				}
			}
		} catch (Exception e) {
			// no status edit was asked
		}

		// new password
		try {
			if (LMUtils.getRequestParameterValueNotEmpty(request,
					"changePassword") != null) {
				try {
					String showUserLogin = LMUtils
							.getRequestParameterValueNotEmpty(request,
									"showUserLogin");
					String newPass = LMUtils.getRequestParameterValueNotEmpty(
							request, "new_password");
					String c_newPass = LMUtils
							.getRequestParameterValueNotEmpty(request,
									"c_password");
					if (newPass.equals(c_newPass)) {
						changePassword(showUserLogin, newPass);
						LMUtils.setRequestAttribute(request, "errorPassword",
								"Done!");
						LMUtils.setRequestAttribute(request,
								"new_passwordError", "");
						LMUtils.setRequestAttribute(request, "c_passwordError",
								"");
					} else {
						LMUtils.setRequestAttribute(request, "errorPassword",
								"The passwords don't match");
					}
				} catch (InternalErrorException e) {
					LMUtils.setRequestAttribute(request, "errorPassword", e
							.getMessage());
				} catch (DBException e) {
					LMUtils.setRequestAttribute(request, "errorPassword", e
							.getMessage());
				} catch (MissingParameterException e) {
					LMUtils.setRequestAttribute(request, "errorPassword",
							"Please fill all the required fields");
					LMUtils.setRequestAttribute(request, "new_passwordError",
							"Required field");
					LMUtils.setRequestAttribute(request, "c_passwordError",
							"Required field");
				}
			}
		} catch (Exception e) {
			// no password change was asked
		}

		doGet(request, response);
	}

	/**
	 * Generates permissions string
	 * 
	 * @param showUserLogin
	 * @return
	 * @throws InternalErrorException
	 * @throws DBException
	 */
	private String generatePermissions(String showUserLogin)
			throws InternalErrorException, DBException {

		Connection conn = null;
		PreparedStatement stmt = null;
		ResultSet rs = null;
		try {
			// admin has no permissions
			String rolesTableName = LMUtils.getContextParameterValue(
					getServletContext(), "db.RolesTableName");
			String query = "SELECT * FROM `" + rolesTableName
					+ "` WHERE `Login` = ? AND `RealRole` = 'ADMIN'";
			conn = DBUtils.getConnection();
			stmt = conn.prepareStatement(query);
			stmt.setString(1, showUserLogin);
			rs = stmt.executeQuery();
			if (rs.next()) {
				return "";
			}

			String permissionsTableName = LMUtils.getContextParameterValue(
					getServletContext(), "db.PermissionsTableName");
			String typesTableName = LMUtils.getContextParameterValue(
					getServletContext(), "db.TypesTableName");

			// getting all available permissions
			query = "SELECT * FROM ("
					+ "SELECT `id`, `name`, 'FALSE' as `hasPermission` FROM `"
					+ typesTableName + "` "
					+ "  WHERE `id` NOT IN (SELECT `typeID` FROM `"
					+ permissionsTableName + "` WHERE `userLogin` = ?)"
					+ " UNION "
					+ "SELECT `id`, `name`, 'TRUE' as `hasPermission` FROM `"
					+ typesTableName + "` "
					+ "   WHERE `id` IN (SELECT `typeID` FROM `"
					+ permissionsTableName
					+ "` WHERE `userLogin` = ?)) as A ORDER BY `id`;";

			Template permissionRowTemplate = new Template(
					permissionsRowTemplateName);
			String permissionsText = "";
			stmt = conn.prepareStatement(query);
			stmt.setString(1, showUserLogin);
			stmt.setString(2, showUserLogin);
			rs = stmt.executeQuery();
			while (rs.next()) {
				permissionRowTemplate.addChange("typeID", rs.getString("id"));
				permissionRowTemplate.addChange("typeName", rs
						.getString("name"));
				permissionRowTemplate.addChange("checked", rs
						.getBoolean("hasPermission") ? "CHECKED" : "");
				permissionsText += permissionRowTemplate.applyChanges();
			}

			Template permissionsTemplate = new Template(permissionsTemplateName);
			permissionsTemplate.addChange("permission_rows", permissionsText);
			return permissionsTemplate.applyChanges();
		} catch (SQLException e) {
			throw new DBException("Can't get permissions: " + e.getMessage(), e);
		} finally {
			DBUtils.close(rs);
			DBUtils.close(stmt);
			DBUtils.close(conn);
		}
	}

	/**
	 * Generates Enable / disable string
	 * 
	 * @param userLogin
	 * @param showUserLogin
	 * @return
	 * @throws InternalErrorException
	 * @throws DBException
	 */
	private String generageEnableDisable(String userLogin, String showUserLogin)
			throws InternalErrorException, DBException {
		if (userLogin == null || userLogin.equals(showUserLogin)) {
			return "";
		}
		Connection conn = null;
		PreparedStatement stmt = null;
		ResultSet rs = null;
		try {
			String rolesTableName = LMUtils.getContextParameterValue(
					getServletContext(), "db.RolesTableName");

			conn = DBUtils.getConnection();
			String query = "SELECT `Role` = `RealRole` as `isActive` FROM `"
					+ rolesTableName + "` WHERE `Login` = ?";
			stmt = conn.prepareStatement(query);
			stmt.setString(1, showUserLogin);
			rs = stmt.executeQuery();
			rs.next();

			Template enableDisableTemplate = new Template(
					enableDisableButtonTemplateName);
			if (rs.getBoolean("isActive")) {
				enableDisableTemplate.addChange("active_string", "active");
				enableDisableTemplate.addChange("activeClass", "active");
			} else {
				enableDisableTemplate.addChange("active_string", "blocked");
				enableDisableTemplate.addChange("activeClass", "blocked");
			}
			return enableDisableTemplate.applyChanges();

		} catch (SQLException e) {
			throw new DBException("Can't get permissions: " + e.getMessage(), e);
		} finally {
			DBUtils.close(rs);
			DBUtils.close(stmt);
			DBUtils.close(conn);
		}

	}

	/**
	 * Deletes all old permissions and sets back only new permissions Caution:
	 * There is no check if the user can perform the action
	 * 
	 * @param forUserLogin
	 * @param permissionIDs
	 * @throws InternalErrorException
	 * @throws DBException
	 */
	private void changePermissions(String forUserLogin, String[] permissionIDs)
			throws InternalErrorException, DBException {
		Connection conn = null;
		PreparedStatement stmt = null;
		ResultSet rs = null;
		try {
			String permissionsTableName = LMUtils.getContextParameterValue(
					getServletContext(), "db.PermissionsTableName");
			String query = "BEGIN;";

			conn = DBUtils.getConnection();
			stmt = conn.prepareStatement(query);
			stmt.execute();
			DBUtils.close(stmt);

			// deleting all the permissions
			query = "DELETE FROM `" + permissionsTableName
					+ "` WHERE `userLogin` = ?;";
			stmt = conn.prepareStatement(query);
			stmt.setString(1, forUserLogin);
			stmt.execute();
			DBUtils.close(stmt);

			if (permissionIDs != null) {

				// inserting new permissions
				query = "INSERT INTO `" + permissionsTableName
						+ "` (`userLogin`, `typeID`) VALUES";
				int i;
				for (i = 0; i < permissionIDs.length - 1; i++) {
					query += " (?, ?),";
				}
				if (i < permissionIDs.length) {
					query += " (?, ?);";
					stmt = conn.prepareStatement(query);
					for (int j = 0; j < permissionIDs.length; j++) {
						stmt.setString(2 * j + 1, forUserLogin);
						stmt.setString(2 * j + 2, permissionIDs[j]);
					}
					stmt.execute();
					DBUtils.close(stmt);
				}
			}
			query = "COMMIT;";
			stmt = conn.prepareStatement(query);
			stmt.execute();

		} catch (SQLException e) {
			try {
				conn.prepareStatement("ROLLBACK;").execute();
			} catch (SQLException e1) {
				throw new DBException("Can't set permissions: "
						+ e.getMessage(), e);
			}
			throw new DBException("Can't set permissions: " + e.getMessage(), e);
		} finally {
			DBUtils.close(rs);
			DBUtils.close(stmt);
			DBUtils.close(conn);
		}
	}

	/**
	 * Flips the isActive field of user Caution: there is no check that this
	 * action is legal
	 * 
	 * @param forUserLogin
	 * @throws InternalErrorException
	 * @throws DBException
	 */
	private void changeEnabled(String forUserLogin)
			throws InternalErrorException, DBException {
		Connection conn = null;
		PreparedStatement stmt = null;
		ResultSet rs = null;
		try {
			String rolesTableName = LMUtils.getContextParameterValue(
					getServletContext(), "db.RolesTableName");
			conn = DBUtils.getConnection();

			conn.setAutoCommit(false);

			String query = "SELECT IF (`Role` = `RealRole`, 'BLOCKED', `RealRole`) FROM `"
					+ rolesTableName + "` as A WHERE `Login` = ? LIMIT 1;";
			stmt = conn.prepareStatement(query);
			stmt.setString(1, forUserLogin);
			rs = stmt.executeQuery();
			rs.next();
			String newStatus = rs.getString(1);

			query = "UPDATE `" + rolesTableName + "` SET `Role` = ? "
					+ " WHERE `Login` = ? LIMIT 1;";
			stmt = conn.prepareStatement(query);
			stmt.setString(1, newStatus);
			stmt.setString(2, forUserLogin);
			stmt.execute();

			conn.commit();
		} catch (SQLException e) {
			try {
				conn.rollback();
			} catch (SQLException e1) {
				throw new DBException("Can't change enabled status: "
						+ e.getMessage(), e);
			}
			throw new DBException("Can't change enabled status: "
					+ e.getMessage(), e);
		} finally {
			DBUtils.close(rs);
			DBUtils.close(stmt);
			DBUtils.close(conn);
		}
	}

	/**
	 * Changes user's password
	 * 
	 * @param forUserLogin
	 * @param newPassword
	 * @throws DBException
	 * @throws InternalErrorException
	 */
	private void changePassword(String forUserLogin, String newPassword)
			throws DBException, InternalErrorException {
		Connection conn = null;
		PreparedStatement stmt = null;
		try {
			String userTableName = LMUtils.getContextParameterValue(
					getServletContext(), "db.UsersTableName");
			String query = "UPDATE `" + userTableName
					+ "` SET `Password` = MD5(?) WHERE `Login` = ? LIMIT 1";
			conn = DBUtils.getConnection();
			stmt = conn.prepareStatement(query);
			stmt.setString(1, newPassword);
			stmt.setString(2, forUserLogin);
			stmt.execute();
		} catch (SQLException e) {
			throw new DBException("Can't change password: " + e.getMessage(), e);
		} finally {
			DBUtils.close(stmt);
			DBUtils.close(conn);
		}
	}

	/**
	 * Deletes all future user's reservations
	 * 
	 * @param forUserLogin
	 * @throws DBException
	 * @throws InternalErrorException
	 */
	private void deleteAllReservations(String forUserLogin) throws DBException,
			InternalErrorException {
		Connection conn = null;
		PreparedStatement stmt = null;
		try {
			String reservationsTableName = LMUtils.getContextParameterValue(
					getServletContext(), "db.ReservationsTableName");

			String query = "DELETE FROM `" + reservationsTableName
					+ "` WHERE `userLogin` = ? AND `startTime` > NOW();";
			conn = DBUtils.getConnection();
			stmt = conn.prepareStatement(query);
			stmt.setString(1, forUserLogin);
			stmt.execute();
		} catch (SQLException e) {
			throw new DBException("Can't change password: " + e.getMessage(), e);
		} finally {
			DBUtils.close(stmt);
			DBUtils.close(conn);
		}
	}
}
